﻿/*
 * @Descripttion:
 * @version:
 * @Author: JinYiGao
 * @Date: 2023-01-04 13:12:30
 * @LastEditors: JinYiGao
 * @LastEditTime: 2023-01-04 13:12:30
 */
/*
 * @Descripttion:
 * @version:
 * @Author: JinYiGao
 * @Date: 2022-12-19 17:11:40
 * @LastEditors: JinYiGao
 * @LastEditTime: 2022-12-19 21:00:04
 */

#include <fstream>
#include <iostream>
#include <vector>

#include <tinyxml2/tinyxml2.h>

struct Point {
    float x;
    float y;
};

struct Line {
    Point p1;
    Point p2;
};

// 曲线插值计算t精度
const int seg = 100; // precision = 0.01
// svg图形的宽和高
const int WIDTH = 800;
const int HEIGHT = 600;

void cal_bezier_point(std::vector<Point> &in_pts, std::vector<Point> &out_pts, const float &t) {
    if (in_pts.empty())
        return;
    if (in_pts.size() == 1) {
        out_pts.push_back(in_pts[0]);
        return;
    }
    std::vector<Point> tmp_in; // 存储针对in_pts经过一次插值计算后得到的线段点
    for (int i = 0; i < in_pts.size() - 1; i++) {
        // TODO
        // 代码填写在下方
        // -----------------------------------


        // -----------------------------------
    }
    cal_bezier_point(tmp_in, out_pts, t);
}

void cal_bezier_curve(std::vector<Point> &in_pts, std::vector<Point> &out_pts) {
    for (int i = 0; i <= seg; i++) {
        const float t = i / (float)seg;
        // 以t=0.01的步长插值计算曲线上的点
        cal_bezier_point(in_pts, out_pts, t);
    }
}

tinyxml2::XMLDocument *create_svg(const int &width, const int &height) {
    tinyxml2::XMLDocument *doc = new tinyxml2::XMLDocument();

    // 创建 svg 根标签
    tinyxml2::XMLElement *label_svg = doc->NewElement("svg");
    label_svg->SetAttribute("width", width);
    label_svg->SetAttribute("height", height);
    label_svg->SetAttribute("xmlns", "http://www.w3.org/2000/svg");

    doc->InsertEndChild(label_svg);

    return doc;
}

void destroy(tinyxml2::XMLDocument *doc) {
    delete doc;
    doc = nullptr;
}

void write_lines(tinyxml2::XMLDocument &doc, const std::vector<Point> &pts, const char *color = "red") {
    // TODO
    // 代码填写在下方
    // 注意svg坐标为窗口坐标系，需要转换为笛卡尔坐标系
    // -----------------------------------

    // -----------------------------------
}

void write_points(tinyxml2::XMLDocument &doc, const std::vector<Point> &pts, const int &point_size,
                  const char *color = "black") {
    tinyxml2::XMLElement *label_svg = doc.FirstChildElement("svg");

    tinyxml2::XMLElement *label_point = nullptr;
    for (int i = 0; i < pts.size(); i++) {
        label_point = doc.NewElement("circle");
        label_point->SetAttribute("cx", pts[i].x);
        label_point->SetAttribute("cy", HEIGHT - pts[i].y);
        label_point->SetAttribute("r", point_size);
        label_point->SetAttribute("fill", color);

        label_svg->InsertEndChild(label_point);
        label_point = nullptr;
    }
}

bool save_svg(tinyxml2::XMLDocument &doc, const char *filename) { return doc.SaveFile(filename); }

int main() {
    // 用于贝塞尔曲线计算的原始输入点
    // std::vector<Point> points = {{100, 100}, {200, 200}, {300, 100}, {400, 200}};
    std::vector<Point> points = {{100, 100}, {200, 200}, {150, 250}};
    // 插值曲线结果
    std::vector<Point> out_pts;
    // 计算
    cal_bezier_curve(points, out_pts);

    // -----  svg 绘制 -----
    tinyxml2::XMLDocument *doc = create_svg(WIDTH, HEIGHT);
    write_lines(*doc, points, "blue");
    write_points(*doc, points, 3);
    write_lines(*doc, out_pts, "red");
    save_svg(*doc, "test.svg");
    destroy(doc);

    return 0;
}